# -*- coding: utf-8 -*-
import re
import random
from builtins import range






#Abrimos el fichero
# f=open("fichero.arff")
# 
# #Recorremos todo el fichero y lo almacenamos en "s"
# s=""
# for line in f:
#     if not re.match("^%",line) and not re.match("^\s",line):
#         s = s+line
#     
# f.close()
# 
# #Almacenamos en una lista los atributos(posiciones) con sus valores :
# a = s.splitlines()
# #print (a)
# nAtributos=0
# nValoresDeCadaAtributo = []
# lineasAtributos = []
# for palabra in a:
#     if re.match("^@attribute",palabra):
#         lineasAtributos.append(palabra)
#         nAtributos+=1
#         nValoresDeCadaAtributo.append(palabra.count(',')+1)
# #print("Estos son los atributos :", lineasAtributos)  
# 
# #Le quitamos el ultimo atributo ya que consideramos la hipotesis del mundo cerrado y por lo tanto el atributo del "entonces" no es necesario almacenarlo      
# nAtributos-=1
# nValoresDeCadaAtributo.pop()
# 
# # Esto tenemos que ponerlo con un try cath para que no continue la ejecucion del programa
# if nValoresDeCadaAtributo[len(nValoresDeCadaAtributo)-1] != 2:
#     print("ERROR !!!, fichero arff no valido")
# 
# recortados = []
# for atributo in lineasAtributos:
#         primeraLlave = atributo.index("{")
#         segundaLlave = atributo.index("}")
#         recortados.append(atributo[primeraLlave+1:segundaLlave])
# #recortados.pop(len(recortados)-1)
# #print("Estos son los atributos recortados :", recortados)
# 
# divididos = []
# for recortado in recortados:
#     divididos.append(recortado.split(","))
# 
# listaDeAtributos = []
# for dividido in divididos:
#     subdefinitivo = []
#     for subdividido in dividido: 
#         subdefinitivo.append(subdividido.strip())
#     listaDeAtributos.append(subdefinitivo)
#     
# ###print("Lista de atributos con sus valores: ", listaDeAtributos)
# 
# 
# #Calculamos la longitud de cada regla
# longRegla = 0
# for digito in nValoresDeCadaAtributo:
#     longRegla = longRegla + int(digito)
# 
# ###print ("El numero de atributos es : ", nAtributos)   
# ###print("El numero de valores de cada atributo es: ", nValoresDeCadaAtributo)
# ###print ("Por lo tanto la longitud de cada regla es : ", longRegla)
# 
# 
# #Almacenamos en una lista el conjunto de entrenamiento (@Data) :
# 
# for linea in a:
#     if linea == "@data":
#         indiceData = a.index("@data") 
# 
# conjuntoDeEntrenamiento = []
# for data in a[indiceData+1:len(a)]:
#     conjuntoDeEntrenamiento.append(data)
#   
# #print("Este es el conjuntoDeEntrenamiento: ",conjuntoDeEntrenamiento)  
# 
# conjuntoDeEntrenamientoFinal = []
# for conj in conjuntoDeEntrenamiento:
#     conjuntoDeEntrenamientoFinal.append(conj.split(","))
# 
# ###print("Este es el conjuntoDeEntrenamiento: ",conjuntoDeEntrenamientoFinal)
# 
# 
# 
# #Pasamos a binario el conjunto de entrenamiento :
# 
# # reglaTodoACero = ""
# # for i in range(longRegla):
# #     reglaTodoACero += "0";
#     
# 
# conjuntoDeEntrenamientoBinario = []
# for linea in conjuntoDeEntrenamientoFinal:
#     nueva = ""
#     puntero = 0
#     pos = 0
#     for sublinea in linea:
#         if not linea.index(sublinea) == len(linea)-1:
#             #Solventamos el problema del '?' en el conjunto de entrenamiento
#             if sublinea == '?':
#                 posicion = -1
#             else:
#                 posicion = listaDeAtributos[linea.index(sublinea)].index(sublinea)
#             #print(posicion)
#             if posicion == -1:
#                 nueva += "0"*nValoresDeCadaAtributo[pos]
#             elif puntero+posicion-1 < puntero:
#                 nueva += "1"+"0"*(nValoresDeCadaAtributo[pos]-1)
#                 #print(nueva)
#             else:
#                 nueva += "0"*posicion
#                 nueva += "1"
#                 nueva += "0"*(nValoresDeCadaAtributo[pos]-posicion-1)
#             #print(nueva)
#             puntero += nValoresDeCadaAtributo[pos]
#             pos+=1
#             
#         else:
#             #print("estamos en la class")
#             posicion = listaDeAtributos[linea.index(sublinea)].index(sublinea)
#             #print(posicion)
#             if posicion == 0:
#                 nueva += "0"
#             else:
#                 nueva += "1"
#                 
#         
#     conjuntoDeEntrenamientoBinario.append(nueva)
###print("Conjunto de entrenamiento binario : ",conjuntoDeEntrenamientoBinario)
     

  
# # Creacion de la clase individuo

class Individuo:
#     reglas = []
#     fitness = 0.0
    
    def __init__(self, reglas = [], fitness = 0.0):
        self.reglas = reglas
        self.fitness= fitness
 
    def __str__ (self):
        return str(self.reglas)
    
#     def compara (self,other):
#         if self.fitness < other.fitness:
#             rst = -1
#         elif self.fitness > other.fitness:
#             rst = 1
#         else:
#             rst = 0
#             
#         return rst
   

      



#Metodos auxiliares

# #Metodo que genera una regla al azar 
# def generaReglaAlAzar():
# 
#     reglaGenerada = ""
#  
#     for _ in range(longRegla):
#         reglaGenerada += str(random.randrange(0,2))
#         
#     return reglaGenerada

#print(generaReglaAlAzar());


def encontrarElementoEnLista(elemento,lista):
        try:
            index_element=lista.index(elemento)
            return index_element
        except ValueError:
            return -1



#Pseudocodigo !!!

# INICIAR poblaci�n
# EVALUAR cada individuo de la poblaci�n

# Repetir hasta CONDICI�N_DE_TERMINACI�N
    # SELECCIONAR padres
    # COMBINAR pares de padres
    # MUTAR hijos resultantes
    # EVALUAR nuevos individuos
    # SELECCIONAR individuos para la siguiente generaci�n

# Devolver el mejor de la �ltima generaci�n


# N cromosomas, K maximo n� de reglas en cada cromosoma, T iteraciones

# N = 3
# K= 3


# #Iniciar poblacion :
# 
# def generaPoblacionInicial (N , K):
# 
#     poblacion = []
# #     individuo = Individuo()
# 
#     for i in range(N):
#         individuo = Individuo()
#         individuo.reglas = []
#         nReglas = random.randrange(1,K+1) #Cada uno de los cromosomas tendra este numero aleatorio de reglas.(OJO: cada cromosoma tendra un  numero de reglas distinto).
#         #print(nReglas)
#         for j in range(nReglas):
#             individuo.reglas.append(generaReglaAlAzar())
#             
#         poblacion.append(individuo)
#         
#     return poblacion
# 
# # poblacionInicial = generaPoblacionInicial(N,K)
# # print("La poblacion inicial es: ",poblacionInicial)
# # 
# # for indivi in range (len(poblacionInicial)):
# #     print(poblacionInicial[indivi]) 
# 
# 
# 
# 
# 
# # EVALUAR cada individuo de la poblaci�n
# 
#  
# def evaluarCadaIndividuoDeLaPoblacion (poblacion2, conjuntoDeEntrenamientoBinario):
#     
#         
#     listaDeSatisfacionesDeCadaCromosoma = []
#     for cromosoma in poblacion2:
#         acum = 0
#         basesValidasParaUnCromosoma = []
#         for base in conjuntoDeEntrenamientoBinario:
#             algunaReglaDeEseCromosomaEsValida = False
#             algunaReglaDeEseCromosomaEsValida2 = 'nolose'
#             for regla in cromosoma.reglas:
#                 if base[len(base)-1] == "1":
#                     valida = True
#                     for pos in range(longRegla):
#                         if (regla[pos] == "0" and base[pos] == "1"):
#                             valida = False
#                             break
#                     if valida == True:
#                         algunaReglaDeEseCromosomaEsValida = True
#                         break
#                 
#                 else:
#     
#                     valida = True
#                     for pos in range(longRegla):
#                         
#                         if base[pos] == '1' and regla[pos] == '1':
#                             valida = True
#                         else:
#                             valida = False
#                             break
#                     if valida == True:
#                         algunaReglaDeEseCromosomaEsValida2 = 'no'
#                         break
# 
#             #print("alguna regla de ese cromosoma es valida ?? :", algunaReglaDeEseCromosomaEsValida)
#             basesValidasParaUnCromosoma.append(algunaReglaDeEseCromosomaEsValida)
#             if (base[len(base)-1] == "1" and algunaReglaDeEseCromosomaEsValida == True) or (base[len(base)-1] == "0" and algunaReglaDeEseCromosomaEsValida2 == 'nolose'):
#                 acum += 1
#         #print("baseValida:", basesValidasParaUnCromosoma)
#         listaDeSatisfacionesDeCadaCromosoma.append(acum)
#     #print("listaDeSatisfacionesDeCadaCromosoma : ", listaDeSatisfacionesDeCadaCromosoma)
#     #Ahora le asociamos a cada individuo su fitness:
#     indice = 0
#     for individuo in poblacion2:
#         individuo.fitness = listaDeSatisfacionesDeCadaCromosoma[indice]
#         indice += 1
#     return poblacion2
#     
# 
# # resultadoFuncionEvaluar = evaluarCadaIndividuoDeLaPoblacion(poblacionInicial, conjuntoDeEntrenamientoBinario)
# # print("FuncionEVALUAR cada individuo: ", resultadoFuncionEvaluar)
#     
# # for i in resultadoFuncionEvaluar:
# #     print(i.fitness)
#   
#          
# # poblacionPrueba = [['1010100010'], ['1101000010', '0101100010'], ['1010000101', '0101111111']]
# # conjuntoDeEntrenamientoDePrueba = ['01010010010', '00101010010', '00100101010']
# poblacionPrueba = []
# conjuntoDeEntrenamientoDePrueba = ['01010010011', '00101010011', '00100101011']
# ind1 = Individuo()
# ind1.reglas = ['1010100010']
# ind2 = Individuo()
# ind2.reglas = ['1101000010', '0101100010']
# ind3 = Individuo()
# ind3.reglas = ['1010000101', '0101111111']
# poblacionPrueba.append(ind1)
# poblacionPrueba.append(ind2)
# poblacionPrueba.append(ind3)
# 
# ###print("poblacionPrueba :", poblacionPrueba)  
# ###print("conjuntoDeEntrenamientoDePrueba :", conjuntoDeEntrenamientoDePrueba)     
# 
# #resultadoMetodoFitness = evaluarCadaIndividuoDeLaPoblacion(poblacionPrueba, conjuntoDeEntrenamientoDePrueba)           
#                  
# ###print("metodo fitness: ", resultadoMetodoFitness)
# 
# 
# 
# 
# 
# 
# #Combinar pares de padres
# 
# reglaPadre1 = ['0110000110', '0001110001', '0000001100'] 
# reglaPadre2 = ['0010010001', '1010001001']
# padre1 = Individuo()
# padre1.reglas = reglaPadre1
# padre2 = Individuo()
# padre2.reglas = reglaPadre2
# 
# # padre1 = ['holaadios1', 'feomalotio', 'mongolotia'] 
# # padre2 = ['maricontat', 'preciosoes']
# 
# 
# def combinarParesPadres (padre1, padre2):
#     
#     padre1Plano =""
#     for i in range(len(padre1.reglas)):
#         padre1Plano += padre1.reglas[i]
#     #print("este es el papa1 aplanao ",padre1Plano)
# 
#     padre2Plano =""
#     for i in range(len(padre2.reglas)):
#         padre2Plano += padre2.reglas[i]
#     #print("este es el papa2 aplanao ",padre2Plano)
#      
# #     hijo1 = []
#     hijo1Plano=""
# #     hijo2 = []
#     hijo2Plano=""
#      
# # #     Primero genero un numero aleatorio entre 0 y tamaño del padre 1 para ver en que regla hago el primer corte
#     regla1P1 = random.randrange(0, len(padre1.reglas))
# #     Luego otro numero aleatorio para ver en que regla eligo el segundo corte (tiene que ser mayor o igual (no puede salir en las reglas anteriores))
#     regla2P1 = random.randrange(regla1P1, len(padre1.reglas)) 
# #     Otro numero aleatorio para ver en que posicion de la regla 1 (elegida del primer random)  vamos a cortar
#     
#     posRegla1 = random.randrange(0, longRegla-1)
#     
# #     Otro numero aleatorio para ver en que posicion de la regla 2 (elegida del segundo random)  vamos a cortar
#     
# 
#     posRegla2 = random.randrange(posRegla1+1, longRegla)
#     
#    
# #     -------------------------
#     
# #     Primero genero un numero aleatorio entre 0 y tamaño del padre 2 para ver en que regla hago el primer corte
#     regla1P2 = random.randrange(0, len(padre2.reglas))
# #     Luego otro numero aleatorio para ver en que regla eligo el segundo corte (tiene que ser mayor o igual (no puede salir en las reglas anteriores))
#     regla2P2 = random.randrange(regla1P2, len(padre2.reglas)) 
# #     las posiciones seran las mismas que la del padre 1
# 
# # Primer corte en padre 1 definitivo
#     corte1P1 = regla1P1*longRegla +posRegla1  
# # Segundo corte en padre 1 definitivo
#     corte2P1 = regla2P1*longRegla+posRegla2
# # Primer corte en padre 2 definitivo
#     corte1P2 = regla1P2*longRegla +posRegla1  
# # Segundo corte en padre 2 definitivo
#     corte2P2 = regla2P2*longRegla+posRegla2
#    
# #     print("lalalalalalalallalalal")
# #     print("Esta es la regla1P1",regla1P1)
# #     print("Esta es la regla2P1 ",regla2P1)
# #     print("Posicion de corte regla1 ",posRegla1)
# #     print("Posicion de corte regla2 ",posRegla2)
# #     print("Esta es la regla1P2 ",regla1P2)
# #     print("Esta es la regla2P2 ",regla2P2)
# #     print("Definitivo c1p1 ",corte1P1)
# #     print("Definitivo c2p1 ",corte2P1)
# #     print("Definitivo c1p2 ",corte1P2)
# #     print("Definitivo c2p2 ",corte2P2)
#     
# # vamos recorriendo el padre 1 y vamos agregando al hijo 1 todo hasta que llegamos a la
# # regla1P1 y la posRegla1, entonces vamos a regla1P2, posRegla1 y copiamos todo hasta que
# # lleguemos a regla2P2, posRegla2 y nos vamos a regla2P1, posRegla2 y terminamos de copiar
# 
# # //Con planos. 
# # Vamos a ir recorriendo el padre1plano hasta que llega al corte1P1, entonces copiamos a partir 
# # del corte1P2 hasta que llegamos al corte2P2 y cambiamos a corte2P1 y terminamos de copiar.
# 
# 
# 
#     for unidadP1H1 in padre1Plano[0:corte1P1]:
#         hijo1Plano += unidadP1H1
#     
#     for unidadP2H1 in padre2Plano[corte1P2:corte2P2+1]:
#         hijo1Plano += unidadP2H1
# 
#     for unidadP1AH1 in padre1Plano[corte2P1+1: len(padre1Plano)]:
#         #print("unidadP1A",unidadP1AH1)
#         hijo1Plano += unidadP1AH1
# 
# #     print("Este es el hijo1Plano ",hijo1Plano)
# #     print("len(padre1Plano)",len(padre1Plano))
# #     print("len(padre1Plano+1)",len(padre1Plano)+1)
#         
#     for unidadP2H2 in padre2Plano[0:corte1P2]:
#         hijo2Plano += unidadP2H2
#     
#     for unidadP1H2 in padre1Plano[corte1P1:corte2P1+1]:
#         hijo2Plano += unidadP1H2
# 
#     for unidadP2AH2 in padre2Plano[corte2P2+1: len(padre2Plano)]:
#         hijo2Plano += unidadP2AH2
# 
#     #print("Este es el hijo2Plano ",hijo2Plano)
#     
# #     Creacion de hijos en forma de lista.
#     
#     indice1 = 0
#     indice2 = longRegla
#     reglaHijo1 = []
#     for i in range(int((len(hijo1Plano)/longRegla))):
#         reglaHijo1.append(hijo1Plano[indice1:indice2])
#         indice1 = indice2
#         indice2 += longRegla
#     #print("este es el hijo1 ya terminado:", hijo1)
#     
#     indice3 = 0
#     indice4 = longRegla
#     reglaHijo2 = []
#     for i in range(int((len(hijo2Plano)/longRegla))):
#         reglaHijo2.append(hijo2Plano[indice3:indice4])
#         indice3 = indice4
#         indice4 += longRegla
#     #print("este es el hijo2 ya terminado:", hijo2)
#     hijo1 = Individuo()
#     hijo1.reglas= reglaHijo1
#     
#     hijo2 = Individuo()
#     hijo2.reglas= reglaHijo2
#     #print("Resultado de aplicar el metodo de cruce entre padre1:",padre1,"y padre2:",padre2,":")
#     return hijo1, hijo2
#      
# 
# resultadoDelMetodoCombinarParesDePadres = combinarParesPadres(padre1, padre2)
# ###print("resultadoDelMetodoCombinarParesDePadres: ",resultadoDelMetodoCombinarParesDePadres)
# ###print("hijo1 sin mutar: ",resultadoDelMetodoCombinarParesDePadres[0])
# ###print("hijo2 sin mutar: ",resultadoDelMetodoCombinarParesDePadres[1])
# 
# 
# 
# 
# # MUTAR hijos resultantes
# 
# def mutacion(hijo1, hijo2, probabilidad):
#     numero = random.random()
#     if numero < probabilidad:
#         reglaMutada = ""
# #         print(numero)
# #         print("SI se produce la mutacion")
#         posicionHijoElegido = random.randrange(0,2)
#         hijoElegido = Individuo()
#         if posicionHijoElegido == 0 :
#             hijoElegido = hijo1
#         else:
#             hijoElegido = hijo2    
#         
#         posicionReglaElegida = random.randrange(0,len(hijoElegido.reglas))  
#         posicionBitElegido = random.randrange(0,longRegla)  
#         #print(posicionHijoElegido,posicionReglaElegida,posicionBitElegido)
# #         if resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido] == "0":
#         if hijoElegido.reglas[posicionReglaElegida][posicionBitElegido] == "0":
#             #print("es un 0")
#             
#             reglaMutada =  hijoElegido.reglas[posicionReglaElegida][0:posicionBitElegido]
#             reglaMutada += "1"
#             reglaMutada += hijoElegido.reglas[posicionReglaElegida][posicionBitElegido+1:longRegla]
#             
#             
#             hijoElegido.reglas.pop(posicionReglaElegida)
#             hijoElegido.reglas.insert(posicionReglaElegida,reglaMutada)
#             return hijo1,hijo2
#             
#         if hijoElegido.reglas[posicionReglaElegida][posicionBitElegido] == "1":
#             #print("es un 1")
#             
#             reglaMutada =  hijoElegido.reglas[posicionReglaElegida][0:posicionBitElegido]
#             reglaMutada += "0"
#             reglaMutada += hijoElegido.reglas[posicionReglaElegida][posicionBitElegido+1:longRegla]
#             
#             
#             hijoElegido.reglas.pop(posicionReglaElegida)
#             hijoElegido.reglas.insert(posicionReglaElegida,reglaMutada)
#             return hijo1 ,hijo2
#             
#     else:
#         #print("NO se produce la mutacion")
#         return hijo1,hijo2
#  
#  
# 
# resultadoDelMetodoMutar = mutacion(resultadoDelMetodoCombinarParesDePadres[0], resultadoDelMetodoCombinarParesDePadres[1], 0.9)
# ###print("Resultado de aplicar el metodo de mutacion :", resultadoDelMetodoMutar) 
# ###print("hijo1 mutado ",resultadoDelMetodoMutar[0])
# ###print("hijo2 mutado ",resultadoDelMetodoMutar[1])
# 
# 
# 
# #Es una variante de la anterior (parametro de entrada una poblacion completa y la probabilidad de que algun elemento de esa
# # poblacion mute )
# 
# def mutacion2(poblacion, probabilidad):
#     numero = random.random()
#     if numero < probabilidad:
#         reglaMutada = ""
# #         print(numero)
# #         print("SI se produce la mutacion")
#         posicionHijoElegido = random.randrange(0,len(poblacion))
#         hijoElegido = poblacion[posicionHijoElegido]   
#         
#         posicionReglaElegida = random.randrange(0,len(hijoElegido.reglas))  
#         posicionBitElegido = random.randrange(0,longRegla)  
#         #print(posicionHijoElegido,posicionReglaElegida,posicionBitElegido)
# #         if resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido] == "0":
#         if hijoElegido.reglas[posicionReglaElegida][posicionBitElegido] == "0":
#             #print("es un 0")
#             
#             reglaMutada =  hijoElegido.reglas[posicionReglaElegida][0:posicionBitElegido]
#             reglaMutada += "1"
#             reglaMutada += hijoElegido.reglas[posicionReglaElegida][posicionBitElegido+1:longRegla]
#             
#             
#             hijoElegido.reglas.pop(posicionReglaElegida)
#             hijoElegido.reglas.insert(posicionReglaElegida,reglaMutada)
#             return poblacion
#             
#         if hijoElegido.reglas[posicionReglaElegida][posicionBitElegido] == "1":
#             #print("es un 1")
#             
#             reglaMutada =  hijoElegido.reglas[posicionReglaElegida][0:posicionBitElegido]
#             reglaMutada += "0"
#             reglaMutada += hijoElegido.reglas[posicionReglaElegida][posicionBitElegido+1:longRegla]
#             
#             
#             hijoElegido.reglas.pop(posicionReglaElegida)
#             hijoElegido.reglas.insert(posicionReglaElegida,reglaMutada)
#             return poblacion
#             
#     else:
#         #print("NO se produce la mutacion")
#         return poblacion
#     
#     
# ###print("poblacionPrueba : ", poblacionPrueba)
# # for i in poblacionPrueba:
# #     print(i.reglas)
# #resultado = mutacion2(poblacionPrueba, 1)
# ###print("mutadion 2: ", resultado)
# # for i in resultado:
# #     print(i.reglas)
#     
#     
# # SELECCIONAR individuos para la siguiente generación
# 
# 
# 
# 
# 
#  
# #Metodo DECODIFICA (recibira un cromosoma, el mejor de la ultima poblacion)
# 
# # cromosomaParaDecodificar = ['101111000000000000001000000000100000000010000001101', '101111000000000000001000000000100000000010000001101', '100000000010000000001000111000100000000010000000001']
# conjReglas = ['101111000000000000001000000000100000000010000001101', '101111000000000000001000000000100000000010000001101', '100000000010000000001000111000100000000010000000001']
# cromosomaParaDecodificar = Individuo()
# cromosomaParaDecodificar.reglas = conjReglas
# 
# def decodifica (cromosoma):
#     i = 0
#     cadenaDecodifica = ""
#     while (i < len(cromosoma.reglas)):
#         j = 0
#         pos = 0
#         for contenido in nValoresDeCadaAtributo:
#             indice = 0
#             bool = "false"
#             for atributo in range (contenido):
#                 indice = atributo + j
#                 #bool = "false"
#                 if cromosoma.reglas[i][indice] == "1":
# #                     print("valor de i:", i)
# #                     print("1")
# #                     print("posicion:", listaDeAtributos[pos][atributo])
#                     bool = "true"
#                     cadenaDecodifica +=  listaDeAtributos[pos][atributo] + "  v  "
#             if bool == "true":
#                 cadenaDecodifica = cadenaDecodifica[0:len(cadenaDecodifica)-3]
#                 #print("cadenaAlterada :", cadenaDecodifica)
#                 cadenaDecodifica += "  ^   "
#        
#             pos += 1
#             j += contenido
#         i += 1
#         cadenaDecodifica = cadenaDecodifica[0:len(cadenaDecodifica)-4] + "entonces  " + str(listaDeAtributos[len(listaDeAtributos)-1][1])
#         cadenaDecodifica += "\n"
#     return cadenaDecodifica

            
            
    

###print("Funcion decodifica :\n",decodifica(cromosomaParaDecodificar) )
             
    
#print("poblacion ordenada: ")
# pobPrueba = []
# 
# ind1 = Individuo()
# ind1.fitness = 8.0
# ind1.reglas = ['0000000001']
# pobPrueba.append(ind1)
# ind2 = Individuo()
# ind2.fitness = 15.0
# ind2.reglas = ['0000000010']
# pobPrueba.append(ind2)
# ind3 = Individuo()
# ind3.fitness = 7.0
# ind3.reglas = ['0000000100']
# pobPrueba.append(ind3)
# ind4 = Individuo()
# ind4.fitness = 7.6
# ind4.reglas = ['0000001000']
# pobPrueba.append(ind4)
# ind5 = Individuo()
# ind5.fitness = 7.2
# ind5.reglas = ['0000010000']
# pobPrueba.append(ind5)
# ind6 = Individuo()
# ind6.fitness = 26.7
# ind6.reglas = ['0000100000']
# pobPrueba.append(ind6)
# ind7 = Individuo()
# ind7.fitness = 26.0
# ind7.reglas = ['0001000000']
# pobPrueba.append(ind7)
# ind8 = Individuo()
# ind8.fitness = 29.0
# ind8.reglas = ['0010000000']
# pobPrueba.append(ind8)
# ind9 = Individuo()
# ind9.fitness = 23.0
# ind9.reglas = ['0100000000']
# pobPrueba.append(ind9)
# ind10 = Individuo()
# ind10.fitness = 21.0
# ind10.reglas = ['1000000000']
# pobPrueba.append(ind10)
# 
# 
# 
# newlist = sorted(pobPrueba, key=lambda x: x.fitness, reverse=True)
# 
# # mejor1 = Individuo()
# mejor1 = newlist[0]
# mejor2 = newlist[1]
# mejor3 = newlist[2]
# mejor4 = newlist[3]
# mejor5 = newlist[4]
# mejor6 = newlist[5]

# print(mejor1.fitness)
# print(mejor2.fitness)
# print(mejor3.fitness)
# print(mejor4.fitness)
# print(mejor5.fitness)
# print(mejor6.fitness)
# print(newlist[6].fitness)
# print(newlist[7].fitness)
# print(newlist[8].fitness)
# print(newlist[9].fitness)



# def seleccionaPadres (poblacion):
#     #Cogemos el 20% de los individuos con mayor fitness para cruzarlos entre si:
#     numeroDeIndividuosSeleccionados = int(len(poblacion)*0.2)
#     if numeroDeIndividuosSeleccionados == 0:
#         numeroDeIndividuosSeleccionados = 1
#     individuosElegidos = []
# #     print("numeroDeIndividuosSeleccionados del 20%: ",numeroDeIndividuosSeleccionados)
#     individuosOrdenados = sorted(poblacion, key=lambda x: x.fitness, reverse=True)
#     for i in range(numeroDeIndividuosSeleccionados):
#         individuosElegidos.append(individuosOrdenados[i])
#         
#     #Cogemos el 10% de entre el resto de individuos
#     numeroDeIndividuosSeleccionados2 = int(len(poblacion)*0.1)
# #     print("numeroDeIndividuosSeleccionados del 10%: ",numeroDeIndividuosSeleccionados2)
#     for i in range (numeroDeIndividuosSeleccionados2):
#         numero = random.randrange(numeroDeIndividuosSeleccionados,len(poblacion))
#         individuosElegidos.append(individuosOrdenados[numero])
#     
# #     for i in individuosElegidos:
# #         print("individuos elegidos: ",i.fitness)
#     return individuosElegidos


        
#resultadoDelMetodoSeleccionaPadres = seleccionaPadres(pobPrueba)
###print("Metodo seleccionaPadres: ", resultadoDelMetodoSeleccionaPadres)




# #Genera siguiente poblacion :
#  
# def generaSiguientePoblacion(listaDePadresElegidos, N):
#     poblacionNueva = []
#     #El individuo de mayor fitness pasa a la siguiente generacion para así no empeorar en ningún caso en la generacion siguiente:
#     poblacionNueva.append(listaDePadresElegidos[0])
#      
#     while len(poblacionNueva) < N:
#         individuo1 = random.randrange(0, len(listaDePadresElegidos))
#         individuo2 = random.randrange(0, len(listaDePadresElegidos))
#         lista = combinarParesPadres(listaDePadresElegidos[individuo1],listaDePadresElegidos[individuo2])  
#         poblacionNueva.append(lista[0])
#         poblacionNueva.append(lista[1])
#     #En el caso de que N sea par siempre nos va a generar N+1 individuos, por lo tanto, eliminaremos el ultimo:
#     if N%2 == 0:
#         poblacionNueva.pop()
#         
#     return poblacionNueva



#ResultadoDelMetodoSiguientePoblacion = generaSiguientePoblacion(resultadoDelMetodoSeleccionaPadres)
###print("Metodo generaSiguientePoblacion :", ResultadoDelMetodoSiguientePoblacion)




# INICIAR poblaci�n
# EVALUAR cada individuo de la poblaci�n

# Repetir hasta CONDICI�N_DE_TERMINACI�N
    # SELECCIONAR padres
    # COMBINAR pares de padres
    # MUTAR hijos resultantes
    # EVALUAR nuevos individuos
    # SELECCIONAR individuos para la siguiente generaci�n

# Devolver el mejor de la �ltima generaci�n

###COMIENZO DEL PROGRAMA###

print("\n¡¡¡ Comienzo del programa !!!")

def programa (fichero, k, N, T):
    
    #Abrimos el fichero
    f=open(fichero)

    #Recorremos todo el fichero y lo almacenamos en "s"
    s=""
    for line in f:
        if not re.match("^%",line) and not re.match("^\s",line):
            s = s+line  
    f.close()
    #Almacenamos en una lista los atributos(posiciones) con sus valores :
    a = s.splitlines()
    nAtributos=0
    nValoresDeCadaAtributo = []
    lineasAtributos = []
    for palabra in a:
        if re.match("^@attribute",palabra):
            lineasAtributos.append(palabra)
            nAtributos+=1
            nValoresDeCadaAtributo.append(palabra.count(',')+1)
    #Le quitamos el ultimo atributo ya que consideramos la hipotesis del mundo cerrado y por lo tanto el atributo del "entonces" no es necesario almacenarlo      
    nAtributos-=1
    nValoresDeCadaAtributo.pop()
    recortados = []
    for atributo in lineasAtributos:
            primeraLlave = atributo.index("{")
            segundaLlave = atributo.index("}")
            recortados.append(atributo[primeraLlave+1:segundaLlave])
    divididos = []
    for recortado in recortados:
        divididos.append(recortado.split(","))

    listaDeAtributos = []
    for dividido in divididos:
        subdefinitivo = []
        for subdividido in dividido: 
            subdefinitivo.append(subdividido.strip())
        listaDeAtributos.append(subdefinitivo)
    #Calculamos la longitud de cada regla
    longRegla = 0
    for digito in nValoresDeCadaAtributo:
        longRegla = longRegla + int(digito)
    #Almacenamos en una lista el conjunto de entrenamiento (@Data) :
    for linea in a:
        if linea == "@data":
            indiceData = a.index("@data") 

    conjuntoDeEntrenamiento = []
    for data in a[indiceData+1:len(a)]:
        conjuntoDeEntrenamiento.append(data)

    conjuntoDeEntrenamientoFinal = []
    for conj in conjuntoDeEntrenamiento:
        conjuntoDeEntrenamientoFinal.append(conj.split(","))

    conjuntoDeEntrenamientoBinario = []
    for linea in conjuntoDeEntrenamientoFinal:
        nueva = ""
        puntero = 0
        pos = 0
        for sublinea in linea:
            if not pos == len(linea)-1:
                #Solventamos el problema del '?' en el conjunto de entrenamiento
                if sublinea == '?':
                    posicion = -1
                else:
                    posicion = listaDeAtributos[linea.index(sublinea)].index(sublinea)
                if posicion == -1:
                    nueva += "0"*nValoresDeCadaAtributo[pos]
                elif puntero+posicion-1 < puntero:
                    nueva += "1"+"0"*(nValoresDeCadaAtributo[pos]-1)
                else:
                    nueva += "0"*posicion
                    nueva += "1"
                    nueva += "0"*(nValoresDeCadaAtributo[pos]-posicion-1)
                puntero += nValoresDeCadaAtributo[pos]
                pos+=1
            
            else:
                #print("estamos en la class")
                posicion = listaDeAtributos[linea.index(sublinea)].index(sublinea)
                if posicion == 0:
                    nueva += "0"
                else:
                    nueva += "1"
                
        conjuntoDeEntrenamientoBinario.append(nueva)
        
        
    #Metodos:
    
    #Metodo que genera una regla al azar 
    def generaReglaAlAzar():

        reglaGenerada = ""
 
        for _ in range(longRegla):
            reglaGenerada += str(random.randrange(0,2))
        
        return reglaGenerada
     
     
    #Iniciar poblacion :
    def generaPoblacionInicial (N , K):

        poblacion = []
#     individuo = Individuo()

        for _ in range(N):
            individuo = Individuo()
            individuo.reglas = []
            nReglas = random.randrange(1,K+1) #Cada uno de los cromosomas tendra este numero aleatorio de reglas.(OJO: cada cromosoma tendra un  numero de reglas distinto).
        #print(nReglas)
            for _ in range(nReglas):
                individuo.reglas.append(generaReglaAlAzar())
            
            poblacion.append(individuo)
        
        return poblacion

# poblacionInicial = generaPoblacionInicial(N,K)
# print("La poblacion inicial es: ",poblacionInicial)
# 
# for indivi in range (len(poblacionInicial)):
#     print(poblacionInicial[indivi]) 





# EVALUAR cada individuo de la poblaci�n

 
    def evaluarCadaIndividuoDeLaPoblacion (poblacion2, conjuntoDeEntrenamientoBinario):
    
        
        listaDeSatisfacionesDeCadaCromosoma = []
        for cromosoma in poblacion2:
            acum = 0
            basesValidasParaUnCromosoma = []
            for base in conjuntoDeEntrenamientoBinario:
                algunaReglaDeEseCromosomaEsValida = False
                algunaReglaDeEseCromosomaEsValida2 = 'nolose'
                for regla in cromosoma.reglas:
                    if base[len(base)-1] == "1":
                        valida = True
                        for pos in range(longRegla):
                            if (regla[pos] == "0" and base[pos] == "1"):
                                valida = False
                                break
                        if valida == True:
                            algunaReglaDeEseCromosomaEsValida = True
                            break
                
                    else:
    
                        valida = True
                        for pos in range(longRegla):
                        
                            if base[pos] == '1' and regla[pos] == '1':
                                valida = True
                            else:
                                valida = False
                                break
                        if valida == True:
                            algunaReglaDeEseCromosomaEsValida2 = 'no'
                            break

            #print("alguna regla de ese cromosoma es valida ?? :", algunaReglaDeEseCromosomaEsValida)
                basesValidasParaUnCromosoma.append(algunaReglaDeEseCromosomaEsValida)
                if (base[len(base)-1] == "1" and algunaReglaDeEseCromosomaEsValida == True) or (base[len(base)-1] == "0" and algunaReglaDeEseCromosomaEsValida2 == 'nolose'):
                    acum += 1
            #print("baseValida:", basesValidasParaUnCromosoma)
            listaDeSatisfacionesDeCadaCromosoma.append(acum)
    #print("listaDeSatisfacionesDeCadaCromosoma : ", listaDeSatisfacionesDeCadaCromosoma)
    #Ahora le asociamos a cada individuo su fitness:
        indice = 0
        for individuo in poblacion2:
            individuo.fitness = listaDeSatisfacionesDeCadaCromosoma[indice]/len(conjuntoDeEntrenamientoBinario)
            indice += 1
        return poblacion2
    
    

    #Combinar pares de padres
    def combinarParesPadres (padre1, padre2):
    
        padre1Plano =""
        for i in range(len(padre1.reglas)):
            padre1Plano += padre1.reglas[i]
    #print("este es el papa1 aplanao ",padre1Plano)

        padre2Plano =""
        for i in range(len(padre2.reglas)):
            padre2Plano += padre2.reglas[i]
    #print("este es el papa2 aplanao ",padre2Plano)
     
#     hijo1 = []
        hijo1Plano=""
#     hijo2 = []
        hijo2Plano=""
     
# #     Primero genero un numero aleatorio entre 0 y tamaño del padre 1 para ver en que regla hago el primer corte
        regla1P1 = random.randrange(0, len(padre1.reglas))
#     Luego otro numero aleatorio para ver en que regla eligo el segundo corte (tiene que ser mayor o igual (no puede salir en las reglas anteriores))
        regla2P1 = random.randrange(regla1P1, len(padre1.reglas)) 
#     Otro numero aleatorio para ver en que posicion de la regla 1 (elegida del primer random)  vamos a cortar
    
        posRegla1 = random.randrange(0, longRegla-1)
    
#     Otro numero aleatorio para ver en que posicion de la regla 2 (elegida del segundo random)  vamos a cortar
    

        posRegla2 = random.randrange(posRegla1+1, longRegla)
    
   
#     -------------------------
    
#     Primero genero un numero aleatorio entre 0 y tamaño del padre 2 para ver en que regla hago el primer corte
        regla1P2 = random.randrange(0, len(padre2.reglas))
#     Luego otro numero aleatorio para ver en que regla eligo el segundo corte (tiene que ser mayor o igual (no puede salir en las reglas anteriores))
        regla2P2 = random.randrange(regla1P2, len(padre2.reglas)) 
#     las posiciones seran las mismas que la del padre 1

# Primer corte en padre 1 definitivo
        corte1P1 = regla1P1*longRegla +posRegla1  
# Segundo corte en padre 1 definitivo
        corte2P1 = regla2P1*longRegla+posRegla2
# Primer corte en padre 2 definitivo
        corte1P2 = regla1P2*longRegla +posRegla1  
# Segundo corte en padre 2 definitivo
        corte2P2 = regla2P2*longRegla+posRegla2
   
#     print("lalalalalalalallalalal")
#     print("Esta es la regla1P1",regla1P1)
#     print("Esta es la regla2P1 ",regla2P1)
#     print("Posicion de corte regla1 ",posRegla1)
#     print("Posicion de corte regla2 ",posRegla2)
#     print("Esta es la regla1P2 ",regla1P2)
#     print("Esta es la regla2P2 ",regla2P2)
#     print("Definitivo c1p1 ",corte1P1)
#     print("Definitivo c2p1 ",corte2P1)
#     print("Definitivo c1p2 ",corte1P2)
#     print("Definitivo c2p2 ",corte2P2)
    
# vamos recorriendo el padre 1 y vamos agregando al hijo 1 todo hasta que llegamos a la
# regla1P1 y la posRegla1, entonces vamos a regla1P2, posRegla1 y copiamos todo hasta que
# lleguemos a regla2P2, posRegla2 y nos vamos a regla2P1, posRegla2 y terminamos de copiar

# //Con planos. 
# Vamos a ir recorriendo el padre1plano hasta que llega al corte1P1, entonces copiamos a partir 
# del corte1P2 hasta que llegamos al corte2P2 y cambiamos a corte2P1 y terminamos de copiar.



        for unidadP1H1 in padre1Plano[0:corte1P1]:
            hijo1Plano += unidadP1H1
    
        for unidadP2H1 in padre2Plano[corte1P2:corte2P2+1]:
            hijo1Plano += unidadP2H1

        for unidadP1AH1 in padre1Plano[corte2P1+1: len(padre1Plano)]:
            #print("unidadP1A",unidadP1AH1)
            hijo1Plano += unidadP1AH1

#     print("Este es el hijo1Plano ",hijo1Plano)
#     print("len(padre1Plano)",len(padre1Plano))
#     print("len(padre1Plano+1)",len(padre1Plano)+1)
        
        for unidadP2H2 in padre2Plano[0:corte1P2]:
            hijo2Plano += unidadP2H2
    
        for unidadP1H2 in padre1Plano[corte1P1:corte2P1+1]:
            hijo2Plano += unidadP1H2

        for unidadP2AH2 in padre2Plano[corte2P2+1: len(padre2Plano)]:
            hijo2Plano += unidadP2AH2

    #print("Este es el hijo2Plano ",hijo2Plano)
    
#     Creacion de hijos en forma de lista.
    
        indice1 = 0
        indice2 = longRegla
        reglaHijo1 = []
        for i in range(int((len(hijo1Plano)/longRegla))):
            reglaHijo1.append(hijo1Plano[indice1:indice2])
            indice1 = indice2
            indice2 += longRegla
    #print("este es el hijo1 ya terminado:", hijo1)
    
        indice3 = 0
        indice4 = longRegla
        reglaHijo2 = []
        for i in range(int((len(hijo2Plano)/longRegla))):
            reglaHijo2.append(hijo2Plano[indice3:indice4])
            indice3 = indice4
            indice4 += longRegla
    #print("este es el hijo2 ya terminado:", hijo2)
        hijo1 = Individuo()
        hijo1.reglas= reglaHijo1
    
        hijo2 = Individuo()
        hijo2.reglas= reglaHijo2
    #print("Resultado de aplicar el metodo de cruce entre padre1:",padre1,"y padre2:",padre2,":")
        return hijo1, hijo2
     
     
#Metodo de mutacion
#Es una variante de la anterior (parametro de entrada una poblacion completa y la probabilidad de que algun elemento de esa
# poblacion mute )
    def mutacion2(poblacion, probabilidad):
        numero = random.random()
        if numero < probabilidad:
            reglaMutada = ""
#         print(numero)
#         print("SI se produce la mutacion")
            posicionHijoElegido = random.randrange(0,len(poblacion))
            hijoElegido = poblacion[posicionHijoElegido]   
        
            posicionReglaElegida = random.randrange(0,len(hijoElegido.reglas))  
            posicionBitElegido = random.randrange(0,longRegla)  
        #print(posicionHijoElegido,posicionReglaElegida,posicionBitElegido)
#         if resultadoDelMetodoCombinarParesDePadres[posicionHijoElegido][posicionReglaElegida][posicionBitElegido] == "0":
            if hijoElegido.reglas[posicionReglaElegida][posicionBitElegido] == "0":
            #print("es un 0")
            
                reglaMutada =  hijoElegido.reglas[posicionReglaElegida][0:posicionBitElegido]
                reglaMutada += "1"
                reglaMutada += hijoElegido.reglas[posicionReglaElegida][posicionBitElegido+1:longRegla]
            
            
                hijoElegido.reglas.pop(posicionReglaElegida)
                hijoElegido.reglas.insert(posicionReglaElegida,reglaMutada)
                return poblacion
            
            if hijoElegido.reglas[posicionReglaElegida][posicionBitElegido] == "1":
            #print("es un 1")
            
                reglaMutada =  hijoElegido.reglas[posicionReglaElegida][0:posicionBitElegido]
                reglaMutada += "0"
                reglaMutada += hijoElegido.reglas[posicionReglaElegida][posicionBitElegido+1:longRegla]
            
            
                hijoElegido.reglas.pop(posicionReglaElegida)
                hijoElegido.reglas.insert(posicionReglaElegida,reglaMutada)
                return poblacion
            
        else:
        #print("NO se produce la mutacion")
            return poblacion
 
 
    #Metodo DECODIFICA (recibira un cromosoma, el mejor de la ultima poblacion)
    def decodifica (cromosoma):
        i = 0
        cadenaDecodifica = ""
        while (i < len(cromosoma.reglas)):
            j = 0
            pos = 0
            for contenido in nValoresDeCadaAtributo:
                indice = 0
                bool = "false"
                for atributo in range (contenido):
                    indice = atributo + j
                    #bool = "false"
                    if cromosoma.reglas[i][indice] == "1":
#                     print("valor de i:", i)
#                     print("1")
#                     print("posicion:", listaDeAtributos[pos][atributo])
                        bool = "true"
                        cadenaDecodifica +=  listaDeAtributos[pos][atributo] + "  v  "
                if bool == "true":
                    cadenaDecodifica = cadenaDecodifica[0:len(cadenaDecodifica)-3]
                    #print("cadenaAlterada :", cadenaDecodifica)
                    cadenaDecodifica += "  ^   "
       
                pos += 1
                j += contenido
            i += 1
            cadenaDecodifica = cadenaDecodifica[0:len(cadenaDecodifica)-4] + "entonces  " + str(listaDeAtributos[len(listaDeAtributos)-1][1])
            cadenaDecodifica += "\n"
        return cadenaDecodifica
    
    #Metodo selecciona padres
    def seleccionaPadres (poblacion):
        #Cogemos el 20% de los individuos con mayor fitness para cruzarlos entre si:
        numeroDeIndividuosSeleccionados = int(len(poblacion)*0.2)
        if numeroDeIndividuosSeleccionados == 0:
            numeroDeIndividuosSeleccionados = 1
        individuosElegidos = []
#     print("numeroDeIndividuosSeleccionados del 20%: ",numeroDeIndividuosSeleccionados)
        individuosOrdenados = sorted(poblacion, key=lambda x: x.fitness, reverse=True)
        for i in range(numeroDeIndividuosSeleccionados):
            individuosElegidos.append(individuosOrdenados[i])
        
    #Cogemos el 10% de entre el resto de individuos
        numeroDeIndividuosSeleccionados2 = int(len(poblacion)*0.1)
#     print("numeroDeIndividuosSeleccionados del 10%: ",numeroDeIndividuosSeleccionados2)
        for i in range (numeroDeIndividuosSeleccionados2):
            numero = random.randrange(numeroDeIndividuosSeleccionados,len(poblacion))
            individuosElegidos.append(individuosOrdenados[numero])
    

        return individuosElegidos
    
    
#Genera siguiente poblacion :
    def generaSiguientePoblacion(listaDePadresElegidos, N):
        poblacionNueva = []
        #El individuo de mayor fitness pasa a la siguiente generacion para así no empeorar en ningún caso en la generacion siguiente:
        poblacionNueva.append(listaDePadresElegidos[0])
     
        while len(poblacionNueva) < N:
            individuo1 = random.randrange(0, len(listaDePadresElegidos))
            individuo2 = random.randrange(0, len(listaDePadresElegidos))
            lista = combinarParesPadres(listaDePadresElegidos[individuo1],listaDePadresElegidos[individuo2]) 
#             poblacionNueva.append(lista[0])
#             poblacionNueva.append(lista[1])
#             solventamos problema de hijos con numero de regla mayor a k 
            hijo1 = Individuo()
            hijo2 = Individuo()
            hijo1 = lista[0]
            hijo2 = lista[1]
            
            
            if len(hijo1.reglas) <= k:
#                 print(len(hijo1.reglas))
                poblacionNueva.append(lista[0])
            if len(hijo2.reglas) <= k:    
#                 print(len(hijo2.reglas))
                poblacionNueva.append(lista[1])
        #En el caso de que N sea par siempre nos va a generar N+1 individuos, por lo tanto, eliminaremos el ultimo:
        if N%2 == 0:
            poblacionNueva.pop()
        
        print(len(poblacionNueva))
        return poblacionNueva
    
    
    
    
# INICIAR poblaci�n
# EVALUAR cada individuo de la poblaci�n

# Repetir hasta CONDICI�N_DE_TERMINACI�N
    # SELECCIONAR padres
    # COMBINAR pares de padres
    # MUTAR hijos resultantes
    # EVALUAR nuevos individuos
    # SELECCIONAR individuos para la siguiente generaci�n

# Devolver el mejor de la �ltima generaci�n
    
    
    poblacion = generaPoblacionInicial(N, k)
    #poblacionInicialEvaluada = evaluarCadaIndividuoDeLaPoblacion(poblacion, conjuntoDeEntrenamientoBinario)
    
    iteracion = 0
    while iteracion <= T:
        
        padresSeleccionados = seleccionaPadres(poblacion)
        poblacionSiguiente = generaSiguientePoblacion(padresSeleccionados, N)
#         for i in poblacionSiguiente:
#             print("tamaño de cromosomas:", len(i.reglas))
        poblacionMutada = mutacion2(poblacionSiguiente, 0.01)
        poblacion = evaluarCadaIndividuoDeLaPoblacion(poblacionMutada, conjuntoDeEntrenamientoBinario)
        #padresSeleccionados2 = seleccionaPadres(poblacionEvaluada)
        
        if iteracion == int(10*(T/100)) or iteracion == int(20*(T/100)) or iteracion == int(30*(T/100)) or iteracion == int(40*(T/100)) or iteracion == int(50*(T/100)) or iteracion == int(60*(T/100)) or iteracion == int(70*(T/100)) or iteracion == int(80*(T/100)) or iteracion == int(90*(T/100)) or iteracion == T:
            
            print("Iteracion ", iteracion,":")
            #Valoracion del mejor individuo de la poblacion
            poblacionOrd = sorted(poblacion, key=lambda x: x.fitness, reverse=True)
            print("Valoracion mejor individuo :", poblacionOrd[0].fitness)
            
            #Valoracion media de la poblacion
            valoracion = 0
            for i in poblacionOrd:
                valoracion += i.fitness
            print("Valoracion media :", valoracion/N)
            
            #Valoracion del peor individuo de la poblacion
            print("Valoracion peor individuo :", poblacionOrd[N-1].fitness)
            
        
        iteracion += 1
    
    #Devolver el mejor de la ultima generacion
    poblacionOrdenada = sorted(poblacion, key=lambda x: x.fitness, reverse=True)
    cromosoma = decodifica(poblacionOrdenada[0])
    return cromosoma



print("programa: ", programa("fichero.arff", 10, 10, 100))
    

